#ifndef __FPGA_GATHER_H__
#define __FPGA_GATHER_H__

#include "graph_fpga.h"

#include "fpga_decoder32.h"
#include <stdio.h>

void tuplesPartition1(hls::stream<edge_tuples_t>  &edgeTuples,
		hls::stream<edge_tuples8_t> &output1,
		hls::stream<edge_tuples8_t> &output2,
		hls::stream<edge_tuples8_t> &output3,
		hls::stream<edge_tuples8_t> &output4,
		hls::stream<edge_tuples8_t> &output5,
		hls::stream<edge_tuples8_t> &output6,
		hls::stream<edge_tuples8_t> &output7,
		hls::stream<edge_tuples8_t> &output8){
	while (true)
	{
		#pragma HLS PIPELINE II=1
		edge_tuples_t	tuples;
		read_from_stream(edgeTuples, tuples);

		edge_tuples8_t out1,out2,out3,out4,out5,out6,out7,out8;
		out1.datax=tuples.datax.range(1*8*X_WIDTH-1, 0*8*X_WIDTH);
		out2.datax=tuples.datax.range(2*8*X_WIDTH-1, 1*8*X_WIDTH);
		out3.datax=tuples.datax.range(3*8*X_WIDTH-1, 2*8*X_WIDTH);
		out4.datax=tuples.datax.range(4*8*X_WIDTH-1, 3*8*X_WIDTH);
		out5.datax=tuples.datax.range(5*8*X_WIDTH-1, 4*8*X_WIDTH);
		out6.datax=tuples.datax.range(6*8*X_WIDTH-1, 5*8*X_WIDTH);
		out7.datax=tuples.datax.range(7*8*X_WIDTH-1, 6*8*X_WIDTH);
		out8.datax=tuples.datax.range(8*8*X_WIDTH-1, 7*8*X_WIDTH);

		out1.datay=tuples.datay.range(1*8*Y_WIDTH-1, 0*8*Y_WIDTH);
		out2.datay=tuples.datay.range(2*8*Y_WIDTH-1, 1*8*Y_WIDTH);
		out3.datay=tuples.datay.range(3*8*Y_WIDTH-1, 2*8*Y_WIDTH);
		out4.datay=tuples.datay.range(4*8*Y_WIDTH-1, 3*8*Y_WIDTH);
		out5.datay=tuples.datay.range(5*8*Y_WIDTH-1, 4*8*Y_WIDTH);
		out6.datay=tuples.datay.range(6*8*Y_WIDTH-1, 5*8*Y_WIDTH);
		out7.datay=tuples.datay.range(7*8*Y_WIDTH-1, 6*8*Y_WIDTH);
		out8.datay=tuples.datay.range(8*8*Y_WIDTH-1, 7*8*Y_WIDTH);

		out1.flag=tuples.flag;
		out2.flag=tuples.flag;
		out3.flag=tuples.flag;
		out4.flag=tuples.flag;
		out5.flag=tuples.flag;
		out6.flag=tuples.flag;
		out7.flag=tuples.flag;
		out8.flag=tuples.flag;

		write_to_stream(output1, out1);
		write_to_stream(output2, out2);
		write_to_stream(output3, out3);
		write_to_stream(output4, out4);
		write_to_stream(output5, out5);
		write_to_stream(output6, out6);
		write_to_stream(output7, out7);
		write_to_stream(output8, out8);

		if(tuples.flag==1){
			break;
		}
	}
}
void tuplesPartition2(hls::stream<edge_tuples8_t>  &edgeTuples,
		hls::stream<int2_token_merge> &output1,
		hls::stream<int2_token_merge> &output2,
		hls::stream<int2_token_merge> &output3,
		hls::stream<int2_token_merge> &output4,
		hls::stream<int2_token_merge> &output5,
		hls::stream<int2_token_merge> &output6,
		hls::stream<int2_token_merge> &output7,
		hls::stream<int2_token_merge> &output8){
#pragma HLS function_instantiate variable=edgeTuples
	while (true)
	{
		#pragma HLS PIPELINE II=1
		edge_tuples8_t	tuples;
		read_from_stream(edgeTuples, tuples);

		int2_token_merge out1,out2,out3,out4,out5,out6,out7,out8;
		get_data_x(out1)=tuples.datax.range(1*1*X_WIDTH-1, 0*1*X_WIDTH);
		get_data_x(out2)=tuples.datax.range(2*1*X_WIDTH-1, 1*1*X_WIDTH);
		get_data_x(out3)=tuples.datax.range(3*1*X_WIDTH-1, 2*1*X_WIDTH);
		get_data_x(out4)=tuples.datax.range(4*1*X_WIDTH-1, 3*1*X_WIDTH);
		get_data_x(out5)=tuples.datax.range(5*1*X_WIDTH-1, 4*1*X_WIDTH);
		get_data_x(out6)=tuples.datax.range(6*1*X_WIDTH-1, 5*1*X_WIDTH);
		get_data_x(out7)=tuples.datax.range(7*1*X_WIDTH-1, 6*1*X_WIDTH);
		get_data_x(out8)=tuples.datax.range(8*1*X_WIDTH-1, 7*1*X_WIDTH);

		get_data_y(out1)=tuples.datay.range(1*1*Y_WIDTH-1, 0*1*Y_WIDTH);
		get_data_y(out2)=tuples.datay.range(2*1*Y_WIDTH-1, 1*1*Y_WIDTH);
		get_data_y(out3)=tuples.datay.range(3*1*Y_WIDTH-1, 2*1*Y_WIDTH);
		get_data_y(out4)=tuples.datay.range(4*1*Y_WIDTH-1, 3*1*Y_WIDTH);
		get_data_y(out5)=tuples.datay.range(5*1*Y_WIDTH-1, 4*1*Y_WIDTH);
		get_data_y(out6)=tuples.datay.range(6*1*Y_WIDTH-1, 5*1*Y_WIDTH);
		get_data_y(out7)=tuples.datay.range(7*1*Y_WIDTH-1, 6*1*Y_WIDTH);
		get_data_y(out8)=tuples.datay.range(8*1*Y_WIDTH-1, 7*1*Y_WIDTH);

		get_flag(out1)=tuples.flag;
		get_flag(out2)=tuples.flag;
		get_flag(out3)=tuples.flag;
		get_flag(out4)=tuples.flag;
		get_flag(out5)=tuples.flag;
		get_flag(out6)=tuples.flag;
		get_flag(out7)=tuples.flag;
		get_flag(out8)=tuples.flag;

		if(get_data_y(out1)||tuples.flag) write_to_stream(output1, out1);
		if(get_data_y(out2)||tuples.flag) write_to_stream(output2, out2);
		if(get_data_y(out3)||tuples.flag) write_to_stream(output3, out3);
		if(get_data_y(out4)||tuples.flag) write_to_stream(output4, out4);
		if(get_data_y(out5)||tuples.flag) write_to_stream(output5, out5);
		if(get_data_y(out6)||tuples.flag) write_to_stream(output6, out6);
		if(get_data_y(out7)||tuples.flag) write_to_stream(output7, out7);
		if(get_data_y(out8)||tuples.flag) write_to_stream(output8, out8);

		if(tuples.flag==1){
			break;
		}
	}
}
/*
void shufflePrepare(hls::stream<edge_tuples1_t>  &edgeTuples,
		hls::stream<int2_token_merge> &output){
	while (true)
	{
		#pragma HLS PIPELINE II=1
		edge_tuples1_t	tuples;
		read_from_stream(edgeTuples, tuples);
		if(tuples.flag==1){
			break;
		}
		int2_token_merge token;
		get_data_x(token)=tuples.datax;
		get_data_y(token)=tuples.datay;
		get_flag(token)=0;
		write_to_stream(output, token);
	}
	{
		int2_token_merge token;
		get_data_x(token)=0;
		get_data_y(token)=0;
		get_flag(token)=1;
		write_to_stream(output, token);
	}
}*/

void shuffle4to4(
		int index,//index,(index+16)%64,(index+32)%64,(index+48)%64
		int step,//16
		hls::stream<int2_token_merge> &input1,
		hls::stream<int2_token_merge> &input2,
		hls::stream<int2_token_merge> &input3,
		hls::stream<int2_token_merge> &input4,
		hls::stream<int2_token_merge> (&output1)[4],
		hls::stream<int2_token_merge> (&output2)[4],
		hls::stream<int2_token_merge> (&output3)[4],
		hls::stream<int2_token_merge> (&output4)[4]){
#pragma HLS function_instantiate variable=input1

	ap_uint<1> end_count1=0;
	ap_uint<1> end_count2=0;
	ap_uint<1> end_count3=0;
	ap_uint<1> end_count4=0;
	while (true)
	{
#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count1 inter false
#pragma HLS dependence variable=end_count1 intra false
#pragma HLS dependence variable=end_count2 inter false
#pragma HLS dependence variable=end_count2 intra false
#pragma HLS dependence variable=end_count3 inter false
#pragma HLS dependence variable=end_count3 intra false
#pragma HLS dependence variable=end_count4 inter false
#pragma HLS dependence variable=end_count4 intra false

		int2_token_merge	tuples1;
		int2_token_merge	tuples2;
		int2_token_merge	tuples3;
		int2_token_merge	tuples4;
		ap_uint<3> end_num;
		{
#pragma HLS latency min=3 max=3
			int2_token_merge	tmp_tuples1;
			int2_token_merge	tmp_tuples2;
			int2_token_merge	tmp_tuples3;
			int2_token_merge	tmp_tuples4;
			bool read_state1=input1.read_nb(tmp_tuples1);
			bool read_state2=input2.read_nb(tmp_tuples2);
			bool read_state3=input3.read_nb(tmp_tuples3);
			bool read_state4=input4.read_nb(tmp_tuples4);
			tuples1=(read_state1)?tmp_tuples1:(int2_token_merge)0;
			tuples2=(read_state2)?tmp_tuples2:(int2_token_merge)0;
			tuples3=(read_state3)?tmp_tuples3:(int2_token_merge)0;
			tuples4=(read_state4)?tmp_tuples4:(int2_token_merge)0;

			if((end_count1==1||get_flag(tuples1)==1)
					&&(end_count2==1||get_flag(tuples2)==1)
					&&(end_count3==1||get_flag(tuples3)==1)
					&&(end_count4==1||get_flag(tuples4)==1)){
				break;
			}

			end_count1+=get_flag(tuples1);
			end_count2+=get_flag(tuples2);
			end_count3+=get_flag(tuples3);
			end_count4+=get_flag(tuples4);
			//if(end_count1==1&&end_count2==1&&end_count3==1&&end_count4==1){
				//break;
			//}
		}

		if(get_data_y(tuples1)&&get_data_x(tuples1)%PE_NUM>=(index+0*step)&&get_data_x(tuples1)%PE_NUM<(index+1*step)) write_to_stream(output1[0], tuples1);
		if(get_data_y(tuples1)&&get_data_x(tuples1)%PE_NUM>=(index+1*step)&&get_data_x(tuples1)%PE_NUM<(index+2*step)) write_to_stream(output2[0], tuples1);
		if(get_data_y(tuples1)&&get_data_x(tuples1)%PE_NUM>=(index+2*step)&&get_data_x(tuples1)%PE_NUM<(index+3*step)) write_to_stream(output3[0], tuples1);
		if(get_data_y(tuples1)&&get_data_x(tuples1)%PE_NUM>=(index+3*step)&&get_data_x(tuples1)%PE_NUM<(index+4*step)) write_to_stream(output4[0], tuples1);

		if(get_data_y(tuples2)&&get_data_x(tuples2)%PE_NUM>=(index+0*step)&&get_data_x(tuples2)%PE_NUM<(index+1*step)) write_to_stream(output1[1], tuples2);
		if(get_data_y(tuples2)&&get_data_x(tuples2)%PE_NUM>=(index+1*step)&&get_data_x(tuples2)%PE_NUM<(index+2*step)) write_to_stream(output2[1], tuples2);
		if(get_data_y(tuples2)&&get_data_x(tuples2)%PE_NUM>=(index+2*step)&&get_data_x(tuples2)%PE_NUM<(index+3*step)) write_to_stream(output3[1], tuples2);
		if(get_data_y(tuples2)&&get_data_x(tuples2)%PE_NUM>=(index+3*step)&&get_data_x(tuples2)%PE_NUM<(index+4*step)) write_to_stream(output4[1], tuples2);

		if(get_data_y(tuples3)&&get_data_x(tuples3)%PE_NUM>=(index+0*step)&&get_data_x(tuples3)%PE_NUM<(index+1*step)) write_to_stream(output1[2], tuples3);
		if(get_data_y(tuples3)&&get_data_x(tuples3)%PE_NUM>=(index+1*step)&&get_data_x(tuples3)%PE_NUM<(index+2*step)) write_to_stream(output2[2], tuples3);
		if(get_data_y(tuples3)&&get_data_x(tuples3)%PE_NUM>=(index+2*step)&&get_data_x(tuples3)%PE_NUM<(index+3*step)) write_to_stream(output3[2], tuples3);
		if(get_data_y(tuples3)&&get_data_x(tuples3)%PE_NUM>=(index+3*step)&&get_data_x(tuples3)%PE_NUM<(index+4*step)) write_to_stream(output4[2], tuples3);

		if(get_data_y(tuples4)&&get_data_x(tuples4)%PE_NUM>=(index+0*step)&&get_data_x(tuples4)%PE_NUM<(index+1*step)) write_to_stream(output1[3], tuples4);
		if(get_data_y(tuples4)&&get_data_x(tuples4)%PE_NUM>=(index+1*step)&&get_data_x(tuples4)%PE_NUM<(index+2*step)) write_to_stream(output2[3], tuples4);
		if(get_data_y(tuples4)&&get_data_x(tuples4)%PE_NUM>=(index+2*step)&&get_data_x(tuples4)%PE_NUM<(index+3*step)) write_to_stream(output3[3], tuples4);
		if(get_data_y(tuples4)&&get_data_x(tuples4)%PE_NUM>=(index+3*step)&&get_data_x(tuples4)%PE_NUM<(index+4*step)) write_to_stream(output4[3], tuples4);
	}
	{
		for(int i=0;i<4;i++){
			#pragma HLS UNROLL
			int2_token_merge token;
			get_data_x(token)=0;
			get_data_y(token)=0;
			get_flag(token)=1;
			write_to_stream(output1[i], token);
			write_to_stream(output2[i], token);
			write_to_stream(output3[i], token);
			write_to_stream(output4[i], token);
		}
	}
}

void filter4to1(
		int index,
		hls::stream<int2_token_merge> (&input)[4],
		hls::stream<int2_token_merge> &output){
#pragma HLS function_instantiate variable=input

	ap_uint<2> end_count=0;
	ap_uint<2> cycle_count=0;
	while(true){
		#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count inter false
#pragma HLS dependence variable=end_count intra false

		ap_uint<2> pri=cycle_count;
		cycle_count=(cycle_count+1)%4;

		int2_token_merge	token;
		if((pri==0||input[1].empty()&&input[2].empty()&&input[3].empty())&&!input[0].empty())
			token=input[0].read();
		else if((pri==1||input[2].empty()&&input[3].empty())&&!input[1].empty())
			token=input[1].read();
		else if((pri==2||input[3].empty())&&!input[2].empty())
			token=input[2].read();
		else if(!input[3].empty())
			token=input[3].read();
		else token=0;

		if(end_count==3&&get_flag(token)) break;
		end_count+=get_flag(token);

		if(get_flag(token)==0&&get_data_y(token)){
			write_to_stream(output, token);
		}
	}
	{
		int2_token_merge token;
		get_data_x(token)=0;
		get_data_y(token)=0;
		get_flag(token)=1;
		write_to_stream(output, token);
	}
}
/*
void filter4to1_last(
		int index,
		hls::stream<int2_token_merge> (&input)[4],
		hls::stream<int2_token> &output){
#pragma HLS function_instantiate variable=input

	ap_uint<2> end_count=0;
	ap_uint<2> cycle_count=0;
	while(true){
		#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count inter false
#pragma HLS dependence variable=end_count intra false

		ap_uint<2> pri=cycle_count;
		cycle_count=(cycle_count+1)%4;

		int2_token_merge	token;
		if((pri==0||input[1].empty()&&input[2].empty()&&input[3].empty())&&!input[0].empty())
			token=input[0].read();
		else if((pri==1||input[2].empty()&&input[3].empty())&&!input[1].empty())
			token=input[1].read();
		else if((pri==2||input[3].empty())&&!input[2].empty())
			token=input[2].read();
		else if(!input[3].empty())
			token=input[3].read();
		else token=0;

		if(end_count==3&&get_flag(token)) break;
		end_count+=get_flag(token);

		if(get_flag(token)==0&&get_data_y(token)){
			int2_token token2;
			token2.data.x=get_data_x(token);
			token2.data.y=get_data_y(token);
			token2.flag=0;
			write_to_stream(output, token2);
		}
	}
	{
		int2_token token2;
		token2.data.x=0;
		token2.data.y=0;
		token2.flag=1;
		write_to_stream(output, token2);
	}
}
*/
/*void filter2to1_last(
		int index,
		hls::stream<int2_token_merge> &input1,
		hls::stream<int2_token_merge> &input2,
		hls::stream<int2_token> &output){
#pragma HLS function_instantiate variable=input1

	ap_uint<1> end_count=0;
	ap_uint<1> cycle_count=0;
	while(true){
		#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count inter false
#pragma HLS dependence variable=end_count intra false

		ap_uint<1> pri=cycle_count;
		cycle_count=~cycle_count;

		int2_token_merge	token;
		if((pri==0||input2.empty())&&!input1.empty()) token=input1.read();
		else if(!input2.empty()) token=input2.read();
		else token=0;

		if(end_count==1&&get_flag(token)) break;
		end_count+=get_flag(token);
		//if(end_count==2) break;

		if(get_flag(token)==0){
			int2_token token2;
			token2.data.x=get_data_x(token);
			token2.data.y=get_data_y(token);
			token2.flag=0;
			write_to_stream(output, token2);
		}
	}
	{
		int2_token token2;
		token2.data.x=0;
		token2.data.y=0;
		token2.flag=1;
		write_to_stream(output, token2);
	}
}
*/
/*void filter2to1(
		int index,
		hls::stream<int2_token_merge> &input1,
		hls::stream<int2_token_merge> &input2,
		hls::stream<int2_token_merge> &output){
#pragma HLS function_instantiate variable=input1

	ap_uint<1> end_count=0;
	ap_uint<1> cycle_count=0;
	while(true){
		#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count inter false
#pragma HLS dependence variable=end_count intra false

		ap_uint<1> pri=cycle_count;
		cycle_count=~cycle_count;

		int2_token_merge	token;
		if((pri==0||input2.empty())&&!input1.empty()) token=input1.read();
		else if(!input2.empty()) token=input2.read();
		else token=0;

		if(end_count==1&&get_flag(token)) break;
		end_count+=get_flag(token);
		//if(end_count==2) break;

		if(get_flag(token)==0){
			write_to_stream(output, token);
		}
	}
	{
		int2_token_merge token;
		get_data_x(token)=0;
		get_data_y(token)=0;
		get_flag(token)=1;
		write_to_stream(output, token);
	}
}

void filter2to1_last(
		int index,
		hls::stream<int2_token_merge> &input1,
		hls::stream<int2_token_merge> &input2,
		hls::stream<int2_token> &output){
#pragma HLS function_instantiate variable=input1

	ap_uint<1> end_count=0;
	ap_uint<1> cycle_count=0;
	while(true){
		#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count inter false
#pragma HLS dependence variable=end_count intra false

		ap_uint<1> pri=cycle_count;
		cycle_count=~cycle_count;

		int2_token_merge	token;
		if((pri==0||input2.empty())&&!input1.empty()) token=input1.read();
		else if(!input2.empty()) token=input2.read();
		else token=0;

		if(end_count==1&&get_flag(token)) break;
		end_count+=get_flag(token);
		//if(end_count==2) break;

		if(get_flag(token)==0){
			int2_token token2;
			token2.data.x=get_data_x(token);
			token2.data.y=get_data_y(token);
			token2.flag=0;
			write_to_stream(output, token2);
		}
	}
	{
		int2_token token2;
		token2.data.x=0;
		token2.data.y=0;
		token2.flag=1;
		write_to_stream(output, token2);
	}
}
*/

/*
void filter4to1(
		int index,
		hls::stream<int2_token_merge> (&input)[4],
		hls::stream<int2_token_merge> &output){
#pragma HLS function_instantiate variable=input

	ap_uint<3> end_count=0;
	unsigned int cycle_count=0;
	while(true){
		#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count inter false
#pragma HLS dependence variable=end_count intra false

#pragma HLS dependence variable=input inter false
#pragma HLS dependence variable=input intra false

		cycle_count++;
		unsigned int i=cycle_count;

		ap_uint<4> state;
		ap_uint<8> state_shift;
		ap_uint<2> select;
		{
#pragma HLS latency min=3 max=3
			state.range(0,0)=(!input[0].empty())?1:0;
			state.range(1,1)=(!input[1].empty())?1:0;
			state.range(2,2)=(!input[2].empty())?1:0;
			state.range(3,3)=(!input[3].empty())?1:0;

			ap_uint<8> state_shift_tmp;
			state_shift_tmp.range(3,0)=state;
			state_shift_tmp.range(7,4)=state;
			state_shift=state_shift_tmp>>(i%4);

			if(state_shift.range(0,0)==1) select=(i+0)%4;
			else if(state_shift.range(1,1)==1) select=(i+1)%4;
			else if(state_shift.range(2,2)==1) select=(i+2)%4;
			else select=(i+3)%4;
		}

		int2_token_merge	token;
		if(input[select].read_nb(token)){
			if(end_count==3&&get_flag(token)) break;
			end_count+=get_flag(token);
			//if(end_count==4) break;

			if(get_flag(token)==0){
				write_to_stream(output, token);
			}
		}
	}
	{
		int2_token_merge token;
		get_data_x(token)=0;
		get_data_y(token)=0;
		get_flag(token)=1;
		write_to_stream(output, token);
	}
}

void filter4to1_last(
		int index,
		hls::stream<int2_token_merge> (&input)[4],
		hls::stream<int2_token> &output){
#pragma HLS function_instantiate variable=input

	ap_uint<3> end_count=0;
	unsigned int cycle_count=0;
	while(true){
		#pragma HLS PIPELINE II=1
#pragma HLS dependence variable=end_count inter false
#pragma HLS dependence variable=end_count intra false

#pragma HLS dependence variable=input inter false
#pragma HLS dependence variable=input intra false

		cycle_count++;
		unsigned int i=cycle_count;

		ap_uint<4> state;
		ap_uint<8> state_shift;
		ap_uint<2> select;
		{
#pragma HLS latency min=3 max=3
			state.range(0,0)=(!input[0].empty())?1:0;
			state.range(1,1)=(!input[1].empty())?1:0;
			state.range(2,2)=(!input[2].empty())?1:0;
			state.range(3,3)=(!input[3].empty())?1:0;

			ap_uint<8> state_shift_tmp;
			state_shift_tmp.range(3,0)=state;
			state_shift_tmp.range(7,4)=state;
			state_shift=state_shift_tmp>>(i%4);

			if(state_shift.range(0,0)==1) select=(i+0)%4;
			else if(state_shift.range(1,1)==1) select=(i+1)%4;
			else if(state_shift.range(2,2)==1) select=(i+2)%4;
			else select=(i+3)%4;
		}

		int2_token_merge	token;
		if(input[select].read_nb(token)){
			if(end_count==3&&get_flag(token)) break;
			end_count+=get_flag(token);
			//if(end_count==4) break;

			if(get_flag(token)==0){
				int2_token token2;
				token2.data.x=get_data_x(token);
				token2.data.y=get_data_y(token);
				token2.flag=0;
				write_to_stream(output, token2);
			}
		}
	}
	{
		int2_token token2;
		token2.data.x=0;
		token2.data.y=0;
		token2.flag=1;
		write_to_stream(output, token2);
	}
}
*/


void shuffleEntryN(hls::stream<int2_token_merge>  (&tuples_stage1_in)[PE_NUM],
		hls::stream<int2_token_merge> (&buildArray)[PE_NUM]){
#pragma HLS DATAFLOW

	//hls::stream<int2_token_merge> tuples_stage1_in[PE_NUM];
//#pragma HLS stream variable=tuples_stage1_in depth=8
//#pragma HLS RESOURCE variable=tuples_stage1_in core=FIFO_SRL

	hls::stream<int2_token_merge> tuples_stage1_out[PE_NUM][4];
#pragma HLS stream variable=tuples_stage1_out depth=4
#pragma HLS RESOURCE variable=tuples_stage1_out core=FIFO_SRL

	hls::stream<int2_token_merge> tuples_stage2_in[PE_NUM];
#pragma HLS stream variable=tuples_stage2_in depth=8
#pragma HLS RESOURCE variable=tuples_stage2_in core=FIFO_SRL

	hls::stream<int2_token_merge> tuples_stage2_out[PE_NUM][4];
#pragma HLS stream variable=tuples_stage2_out depth=4
#pragma HLS RESOURCE variable=tuples_stage2_out core=FIFO_SRL

	hls::stream<int2_token_merge> tuples_stage3_in[PE_NUM];
#pragma HLS stream variable=tuples_stage3_in depth=8
#pragma HLS RESOURCE variable=tuples_stage3_in core=FIFO_SRL

	hls::stream<int2_token_merge> tuples_stage3_out[PE_NUM][4];
#pragma HLS stream variable=tuples_stage3_out depth=4
#pragma HLS RESOURCE variable=tuples_stage3_out core=FIFO_SRL

	//shufflePrepare(edgeTuples,tuples_stage1_in);
	//for(int i=0;i<64;i++){
		//#pragma HLS UNROLL
		//shufflePrepare(edgeTuples[i],tuples_stage1_in[i]);
	//}

	for(int i=0;i<1;i++){
		#pragma HLS UNROLL
		for(int j=0;j<16;j++){
			#pragma HLS UNROLL
			shuffle4to4(i*64,16,
				tuples_stage1_in[i*64+j+0*16],
				tuples_stage1_in[i*64+j+1*16],
				tuples_stage1_in[i*64+j+2*16],
				tuples_stage1_in[i*64+j+3*16],
				tuples_stage1_out[i*64+j+0*16],
				tuples_stage1_out[i*64+j+1*16],
				tuples_stage1_out[i*64+j+2*16],
				tuples_stage1_out[i*64+j+3*16]);
		}
	}
	for(int i=0;i<PE_NUM;i++){
#pragma HLS UNROLL
		filter4to1(i,
				tuples_stage1_out[i],
				tuples_stage2_in[i]);
	}

	for(int i=0;i<4;i++){
#pragma HLS UNROLL
		for(int j=0;j<4;j++){
#pragma HLS UNROLL
			shuffle4to4(i*16,4,
				tuples_stage2_in[i*16+j+0*4],
				tuples_stage2_in[i*16+j+1*4],
				tuples_stage2_in[i*16+j+2*4],
				tuples_stage2_in[i*16+j+3*4],
				tuples_stage2_out[i*16+j+0*4],
				tuples_stage2_out[i*16+j+1*4],
				tuples_stage2_out[i*16+j+2*4],
				tuples_stage2_out[i*16+j+3*4]);
		}
	}
	for(int i=0;i<PE_NUM;i++){
#pragma HLS UNROLL
		filter4to1(i,
				tuples_stage2_out[i],
				tuples_stage3_in[i]);
	}

	for(int i=0;i<16;i++){
	#pragma HLS UNROLL
		for(int j=0;j<1;j++){
			#pragma HLS UNROLL
			shuffle4to4(i*4,1,
				tuples_stage3_in[i*4+j+0*1],
				tuples_stage3_in[i*4+j+1*1],
				tuples_stage3_in[i*4+j+2*1],
				tuples_stage3_in[i*4+j+3*1],
				tuples_stage3_out[i*4+j+0*1],
				tuples_stage3_out[i*4+j+1*1],
				tuples_stage3_out[i*4+j+2*1],
				tuples_stage3_out[i*4+j+3*1]);
		}
	}
	for(int i=0;i<PE_NUM;i++){
	#pragma HLS UNROLL
		filter4to1(i,
			tuples_stage3_out[i],
			buildArray[i]);
	}
}


#endif /* __FPGA_GATHER_H__ */
